﻿// ****************************************************************************
// <copyright file="WeakActionGeneric.cs" company="GalaSoft Laurent Bugnion">
// Copyright © GalaSoft Laurent Bugnion 2009-2011
// </copyright>
// ****************************************************************************
// <author>Laurent Bugnion</author>
// <email>laurent@galasoft.ch</email>
// <date>18.9.2009</date>
// <project>GalaSoft.MvvmLight</project>
// <web>http://www.galasoft.ch</web>
// <license>
// See license.txt in this solution or http://www.galasoft.ch/license_MIT.txt
// </license>
// ****************************************************************************
using System;
using System.Diagnostics.CodeAnalysis;

namespace TimersXP.Helpers
{
    /// <summary>Stores an Action without causing a hard reference to be created to the Action's owner. The owner can be garbage collected at any time.</summary>
    /// <typeparam name="T">The type of the Action's parameter.</typeparam>
    class WeakAction<T> : WeakAction, IExecuteWithObject
    {
        #region Private Class Members
#if SILVERLIGHT
        /// <summary>The action</summary>
        private Action<T> action;
#endif
        /// <summary>The static action</summary>
        private Action<T> staticAction;
        #endregion Private Class Members

        #region Public Properties
        /// <summary>Gets the name of the method that this WeakAction represents.</summary>
        /// <value>The name of the method.</value>
        public override string MethodName
        {
            get
            {
                if (this.staticAction != null)
                {
#if NETFX_CORE
                    return this.staticAction.GetMethodInfo().Name;
#else
                    return this.staticAction.Method.Name;
#endif
                }

#if SILVERLIGHT
                if (this.action != null)
                    return this.action.Method.Name;

                if (Method != null)
                    return Method.Name;

                return string.Empty;
#else
                return Method.Name;
#endif
            }
        }

        /// <summary>Gets a value indicating whether the Action's owner is still alive, or if it was collected by the Garabage Collector already.</summary>
        /// <value><c>true</c> if [is alive]; otherwise, <c>false</c>.</value>
        public override bool IsAlive
        {
            get
            {
                if (this.staticAction == null && Reference == null)
                    return false;

                if (this.staticAction != null)
                {
                    if (Reference != null)
                        return Reference.IsAlive;
                    return true;
                }
                return Reference.IsAlive;
            }
        }
        #endregion Public Properties

        #region Constructors
        /// <summary>Initializes a new instance of the <see cref="WeakAction{T}"/> class.</summary>
        /// <param name="action">The action that will be associated to this instance.</param>
        public WeakAction(Action<T> action) : this(action == null ? null : action.Target, action) { }

        /// <summary>Initializes a new instance of the <see cref="WeakAction{T}"/> class.</summary>
        /// <param name="target">The action's owner.</param>
        /// <param name="action">The action that will be associated to this instance.</param>
        [SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "1", Justification = "Method should fail with an exception if action is null.")]
        public WeakAction(object target, Action<T> action)
        {
#if NETFX_CORE
            if (action.GetMethodInfo().IsStatic)
#else
            if (action.Method.IsStatic)
#endif
            {
                this.staticAction = action;
                if (target != null)
                {
                    // Keep a reference to the target to control the Weak Action's lifetime.
                    Reference = new WeakReference(target);
                }
                return;
            }

#if SILVERLIGHT
            if (!action.Method.IsPublic || (target != null && !target.GetType().IsPublic && !target.GetType().IsNestedPublic))
                this.action = action;
            else
            {
                var name = action.Method.Name;
                if (name.Contains("<") && name.Contains(">"))
                    this.action = action;
                else
                {
                    Method = action.Method;
                    ActionReference = new WeakReference(action.Target);
                }
            }
#else
#if NETFX_CORE
            Method  action.GetMethodInfo();
#else
            Method = action.Method;
#endif
            ActionReference = new WeakReference(action.Target);
#endif
            Reference = new WeakReference(target);
        }
        #endregion Constructors

        #region Public Functions
        /// <summary>Executes the action. This only happens if the action's owner is still alive. The action's parameter is set to default(T).</summary>
        public new void Execute() { Execute(default(T)); }

        /// <summary>Executes the action. This only happens if the action's owner is still alive.</summary>
        /// <param name="parameter">A parameter to be passed to the action.</param>
        public void Execute(T parameter)
        {
            if (this.staticAction != null)
            {
                this.staticAction(parameter);
                return;
            }
            
            var actionTarget = ActionTarget;

            if (IsAlive)
            {
                if (Method != null && ActionReference != null && actionTarget != null)
                    Method.Invoke(actionTarget, new object[] { parameter });
#if SILVERLIGHT
                if (this.action != null)
                {
                    this.action(parameter);
                    return;
                }
#endif
            }
        }

        /// <summary>
        /// Executes the action with a parameter of type object. This parameter will be casted to T. This method implements <see cref="IExecuteWithObject.ExecuteWithObject"/>
        /// and can be useful if you store multiple WeakAction{T} instances but don't know in advance what type T represents.
        /// </summary>
        /// <param name="parameter">The parameter that will be passed to the action after being casted to T.</param>
        public void ExecuteWithObject(object parameter)
        {
            var parameterCasted = (T)parameter;
            Execute(parameterCasted);
        }

        /// <summary>Sets all the actions that this WeakAction contains to null, which is a signal for containing objects that this WeakAction should be deleted.</summary>
        public new void MarkForDeletion()
        {
#if SILVERLIGHT
            this.action = null;
#endif
            this.staticAction = null;
            base.MarkForDeletion();
        }
        #endregion Public Functions
    }
}
